Skip to content

Conversation

@kyakdan
Copy link
Member

@kyakdan kyakdan commented Feb 3, 2026

Summary

Add a hill-climbing maximize() API to Jazzer that guides the fuzzer toward maximizing a value over time. This enables fuzzing scenarios where standard code coverage provides insufficient guidance, such as finding inputs that maximize some computed metric.

Changes

Jazzer.maximize() API

// Guide fuzzer to maximize 'value' within [minValue, maxValue]
Jazzer.maximize(value, id, minValue, maxValue);

// Convenience overload with auto-generated call-site ID (requires instrumentation)
Jazzer.maximize(value, minValue, maxValue);

How it works: For each observed value v, sets coverage counters [0, v-minValue] to 1. This creates incremental progress feedback - higher values trigger more "coverage," guiding the fuzzer toward the maximum. Corpus minimization naturally retains only the input producing the highest value.

Example

Added ReactorFuzzTest demonstrating the API on a chaotic feedback system where standard coverage is constant but the fuzzer needs to maximize a computed temperature value.

CountersTracker provides a flexible API for mapping program state to
coverage counters, enabling incremental progress feedback to libFuzzer.

Key features:
- ensureCountersAllocated(id, numCounters): allocate counter range
- setCounter/setCounterRange: set counter values by ID and offset
- Thread-safe allocation via ConcurrentHashMap
- Separate memory region from main coverage map

This lays the foundation for the maximize() hill-climbing API.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a maximize() API to Jazzer that enables hill-climbing fuzzing scenarios where standard code coverage is insufficient. The API guides the fuzzer to maximize a value by setting coverage counters for all values from the minimum up to the observed value, creating incremental progress feedback.

Changes:

  • Added CountersTracker infrastructure (Java and C++) to manage extra coverage counters separate from regular code coverage
  • Added Jazzer.maximize() API with automatic call-site ID generation via instrumentation hooks
  • Added comprehensive test coverage for the new APIs
  • Added ReactorFuzzTest example demonstrating the maximize API on a chaotic feedback system

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/main/native/com/code_intelligence/jazzer/driver/counters_tracker.h Refactored header from CoverageTracker to CountersTracker, adding support for separate extra counters region
src/main/native/com/code_intelligence/jazzer/driver/counters_tracker.cpp New implementation managing both coverage and extra counters with libFuzzer registration
src/main/native/com/code_intelligence/jazzer/driver/BUILD.bazel Updated build dependencies to reference counters_tracker instead of coverage_tracker
src/main/java/com/code_intelligence/jazzer/runtime/CountersTracker.java New Java class providing thread-safe counter allocation and management API
src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel Added CountersTracker build target and dependencies
src/main/java/com/code_intelligence/jazzer/runtime/JazzerApiHooks.java Added instrumentation hook to auto-generate call-site IDs for maximize() calls
src/main/java/com/code_intelligence/jazzer/api/Jazzer.java Added maximize() API methods with documentation
src/test/java/com/code_intelligence/jazzer/runtime/CountersTrackerTest.java Comprehensive unit tests for CountersTracker including concurrency tests
src/test/java/com/code_intelligence/jazzer/api/MaximizeTest.java Unit tests for the maximize() API covering edge cases
examples/junit/src/test/java/com/example/ReactorFuzzTest.java Example demonstrating maximize() on a temperature maximization problem

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

return;
}

int numCounters = (int) (maxValue - minValue + 1);
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Integer overflow vulnerability: When (maxValue - minValue + 1) exceeds Integer.MAX_VALUE, the cast to int will silently overflow, leading to incorrect behavior. This could result in negative numCounters values or other unexpected results. The code should validate that the range fits within an int before the cast, and provide a clear error message if it doesn't. For example: if (maxValue - minValue + 1 > Integer.MAX_VALUE) throw an appropriate exception.

Suggested change
int numCounters = (int) (maxValue - minValue + 1);
final long range;
try {
range = Math.addExact(Math.subtractExact(maxValue, minValue), 1L);
} catch (ArithmeticException e) {
throw new IllegalArgumentException(
"maxValue - minValue + 1 must fit within a 32-bit signed integer range", e);
}
if (range <= 0L || range > Integer.MAX_VALUE) {
throw new IllegalArgumentException(
"maxValue - minValue + 1 must be in the range [1, " + Integer.MAX_VALUE + "], but was "
+ range);
}
int numCounters = (int) range;

Copilot uses AI. Check for mistakes.

// Set counters if value provides signal
if (value >= minValue) {
int toOffset = (int) (Math.min(value, maxValue) - minValue);
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Integer overflow in toOffset calculation: Similar to the numCounters calculation above, when (Math.min(value, maxValue) - minValue) exceeds Integer.MAX_VALUE, the cast to int will silently overflow. This could cause incorrect counter ranges to be set. Add validation before the cast.

Suggested change
int toOffset = (int) (Math.min(value, maxValue) - minValue);
long toOffsetLong = Math.min(value, maxValue) - minValue;
if (toOffsetLong > Integer.MAX_VALUE) {
// Avoid integer overflow when converting to int; cannot represent this offset safely.
return;
}
int toOffset = (int) toOffsetLong;

Copilot uses AI. Check for mistakes.
Comment on lines 78 to 79
Jazzer.maximize(50, 5008, 0, 100);
Jazzer.maximize(50, 5008, 0, 200); // No-op without runtime
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test issue: This test expects different behavior with and without runtime but doesn't verify either behavior. With runtime, calling maximize with different ranges for the same ID should throw an IllegalArgumentException (as stated in the comment on line 76), but the test doesn't verify this. Consider using an appropriate assertion or expectation to verify the exception is thrown when runtime is available.

Copilot uses AI. Check for mistakes.
Comment on lines 108 to 110
long largeValue = 750_000;
long step = largeValue < 0 ? 0 : Math.min(largeValue / 1000, 1000);
Jazzer.maximize(step, 5014, 0, 1000);
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test is always false.

Suggested change
long largeValue = 750_000;
long step = largeValue < 0 ? 0 : Math.min(largeValue / 1000, 1000);
Jazzer.maximize(step, 5014, 0, 1000);
for (long largeValue : new long[] {-750_000L, 750_000L}) {
long step = largeValue < 0 ? 0 : Math.min(largeValue / 1000, 1000);
Jazzer.maximize(step, 5014, 0, 1000);
}

Copilot uses AI. Check for mistakes.
Comment on lines 51 to 55
/** Maximum number of counters available (default 1M, configurable via environment variable). */
private static final int MAX_COUNTERS =
System.getenv(ENV_MAX_COUNTERS) != null
? Integer.parseInt(System.getenv(ENV_MAX_COUNTERS))
: 1 << 20;

Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential uncaught 'java.lang.NumberFormatException'.

Suggested change
/** Maximum number of counters available (default 1M, configurable via environment variable). */
private static final int MAX_COUNTERS =
System.getenv(ENV_MAX_COUNTERS) != null
? Integer.parseInt(System.getenv(ENV_MAX_COUNTERS))
: 1 << 20;
private static final int DEFAULT_MAX_COUNTERS = 1 << 20;
/** Maximum number of counters available (default 1M, configurable via environment variable). */
private static final int MAX_COUNTERS = initMaxCounters();
private static int initMaxCounters() {
String value = System.getenv(ENV_MAX_COUNTERS);
if (value == null || value.isEmpty()) {
return DEFAULT_MAX_COUNTERS;
}
try {
return Integer.parseInt(value.trim());
} catch (NumberFormatException e) {
return DEFAULT_MAX_COUNTERS;
}
}

Copilot uses AI. Check for mistakes.
Add Jazzer.maximize(value, id, minValue, maxValue) for guiding the
fuzzer to maximize a value over time. For each observed value v in
[minValue, maxValue], sets counters [0, v-minValue] to signal progress.

Features:
- Enables corpus minimization (only max-value input retained)
- Convenience overload without explicit ID (uses instrumentation hook)
- Delegates to CountersTracker for counter management
- No state in Jazzer.java - all managed by CountersTracker
Example shows how maximize() helps fuzz a chaotic feedback system
where standard coverage provides no guidance. The fuzzer is guided
to increase "temperature" through complex state-dependent logic.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +272 to +279
if (maxValue < minValue) {
throw new IllegalArgumentException("maxValue must be >= minValue");
}
long range = maxValue - minValue;
if (range < 0 || range > (long) Integer.MAX_VALUE - 1) {
throw new IllegalArgumentException(
"Range too large: (maxValue - minValue + 1) must be <= Integer.MAX_VALUE");
}
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation checks for invalid ranges occur before checking if the runtime is available (COUNTERS_TRACKER_ALLOCATE == null). This causes exceptions to be thrown even when running without the Jazzer runtime, which is inconsistent with the behavior of similar methods like exploreState() and guideTowardsContainment() that return early when the runtime is unavailable. The validation should be moved after the null check, or wrapped in a try-catch that returns silently, to ensure the method is a no-op when the runtime is not available.

Copilot uses AI. Check for mistakes.
Comment on lines +101 to +102
endOffset = startOffset + numCounters;
if (endOffset > MAX_COUNTERS) {
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential integer overflow on line 101: the expression startOffset + numCounters can overflow if both values are large enough. For example, if startOffset is near Integer.MAX_VALUE and numCounters is positive, the addition will overflow to a negative value, bypassing the check on line 102. This should use a check like if (startOffset > MAX_COUNTERS - numCounters) or cast to long before adding.

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +70
@Test
public void testMaximizeInvalidRange() {
// maxValue < minValue - without runtime this is a no-op,
// with runtime CountersTracker catches "numCounters must be positive"
Jazzer.maximize(50, 5007, 100, 0);
}
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test comment states this should be "a no-op" without runtime, but the actual behavior is that Jazzer.maximize() throws an IllegalArgumentException at line 273 before checking if the runtime is available. This test will fail when the runtime is loaded. Either the test needs to expect an exception, or the validation logic in maximize() needs to be moved after the null check for COUNTERS_TRACKER_ALLOCATE.

Copilot uses AI. Check for mistakes.
Comment on lines +80 to +83
@Test
public void testMaximizeLargeRange() {
// Extremely large range - without runtime this is a no-op,
// with runtime would fail in CountersTracker
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test comment states this would "fail in CountersTracker" with runtime, but it will actually fail earlier in Jazzer.maximize() at line 277 when checking if the range is too large. The validation happens before checking if the runtime is available, so this test will throw an IllegalArgumentException with the message about range being too large, regardless of whether CountersTracker is available.

Suggested change
@Test
public void testMaximizeLargeRange() {
// Extremely large range - without runtime this is a no-op,
// with runtime would fail in CountersTracker
@Test(expected = IllegalArgumentException.class)
public void testMaximizeLargeRange() {
// Extremely large range - Jazzer.maximize validates the range size
// and throws IllegalArgumentException before interacting with CountersTracker

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants